package model1

import (
	"fmt"
	"strings"
)

/*
匿名函数和闭包
匿名函数
函数当然还可以作为返回值，但是在Go语言中函数内部不能再像之前那样定义函数了，只能定义匿名函数。匿名函数就是没有函数名的函数，匿名函数的定义格式如下：

func(参数)(返回值){
    函数体
}
*/
//匿名函数因为没有函数名，所以没办法像普通函数那样调用，所以匿名函数需要保存到某个变量或者作为立即执行函数:
func AnonymousFunc() {
	add := func(x, y int) {
		fmt.Printf("(x + y): %v\n", (x + y))
	}
	add(100, 200) // 通过变量调用匿名函数
	//自执行函数：匿名函数定义完加()直接执行
	func(x, y int) {
		fmt.Printf("(x + y): %v\n", (x + y))
	}(12, 13)
	//匿名函数多用于实现回调函数和闭包
}

/*
闭包
闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说，闭包=函数+引用环境。 首先我们来看一个例子：
*/
func addr() func(int) int {
	var x int
	return func(y int) int {
		x += y
		return x
	}
}

func TestAddr() {
	f := addr()
	fmt.Printf("f(10): %v\n", f(10)) //10
	fmt.Printf("f(20): %v\n", f(20)) //30
	fmt.Printf("f(30): %v\n", f(30)) //60
	f1 := addr()
	fmt.Printf("f1(20): %v\n", f1(20)) //20
	fmt.Printf("f1(30): %v\n", f1(30)) //50
}

//变量f是一个函数并且它引用了其外部作用域中的x变量，此时f就是一个闭包。
//在f的生命周期内，变量x也一直有效。 闭包进阶示例1：
func addr2(x int) func(int) int {
	return func(y int) int {
		x += y
		return x
	}
}

func TestAddr2() {
	x := 20
	f := addr2(x)
	fmt.Printf("f(10): %v\n", f(10)) //10+x
	fmt.Printf("f(20): %v\n", f(20)) //30+x
	fmt.Printf("f(30): %v\n", f(30)) //60+x
	f1 := addr2(x)
	fmt.Printf("f1(20): %v\n", f1(20)) //20+x
	fmt.Printf("f1(30): %v\n", f1(30)) //50+x
}

//闭包进阶示例2：
func makeSuffixFunc(suffix string) func(string) string {
	return func(name string) string {
		if !strings.HasSuffix(name, suffix) {
			return name + suffix
		}
		return name
	}

}
func TestMakeSuffixFunc() {
	jpgFunc := makeSuffixFunc(".jpg")
	txtFunc := makeSuffixFunc(".txt")
	fmt.Printf("jpgFunc(\"test\"): %v\n", jpgFunc("test")) //test.jpg
	fmt.Printf("txtFunc(\"test\"): %v\n", txtFunc("test")) //test.txt
}

//闭包进阶示例3：
func calc(base int) (func(int) int, func(int) int) {
	return func(i int) int {
			base += i
			return base
		}, func(i int) int {
			base -= i
			return base
		}
}
func TestCalc() {
	fun1, fun2 := calc(10)
	fmt.Printf("fun1(10): %v , fun2(10): %v \n", fun1(10), fun2(10)) //20 ,10
	fmt.Printf("fun1(10): %v , fun2(5): %v \n", fun1(10), fun2(5))   //20 ,15
}

//闭包其实并不复杂，只要牢记闭包=函数+引用环境
